%%%-------------------------------------------------------------------
%%% File    : glowser.erl
%%% Author  :  <vjache>
%%% Description : 
%%%
%%% Created : 16 Jun 2009 by  <vjache>
%%%-------------------------------------------------------------------
-module(glowser).

-include("../include/glog.hrl").

%% API
-export([select2tty/2,select2file/3,help/0,h/0]).

%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: 
%% Description:
%%--------------------------------------------------------------------

select2tty(N,WishList) when is_list(WishList) ->
	select2dev(standard_io,N,mpl_parse(WishList));
select2tty(N,Level) when is_atom(Level) ->
	select2tty(N,fun(Entry)->
						svc_glog:pf_level(Entry,Level)
				end);
select2tty(N,PredFun) when is_function(PredFun, 1) ->
	select2dev(standard_io,N,PredFun).

select2file(File,N,WishList) when is_list(WishList) ->
	select2file(File,N,mpl_parse(WishList));
select2file(File,N,Level) when is_atom(Level) ->
	select2file(File, N,fun(Entry)->
						svc_glog:pf_level(Entry,Level)
				end);
select2file(File,N,PredFun) when is_function(PredFun, 1) ->
	ok=filelib:ensure_dir(File),
	{ok, IoDevice}=file:open(File, [write]),
	select2dev(IoDevice,N,PredFun),
	file:close(IoDevice).
	
select2dev(Dev,N,PredFun) when is_function(PredFun, 1) ->
	Total=lists:foldl(fun(Entry,Count)->
							  io:format(Dev,"# ~p",[Count]),
							  io:format(Dev,"~s~n",[lists:flatten(svc_glog:format_entry(Entry))]),
							  Count+1
					  end,1,select(N,PredFun)),
	io:format(Dev,"### Totaly selected ~p ###~n",[Total-1]).

h() ->
	help().
help() ->
	Hlp=["=== Help on Global Log Browser ===\n",
		 "Select last N messages to tty:\n",
		 "\tselect2tty(N,WishList)\n",
		 "\tselect2tty(N,Level)\n",
		 "\tselect2tty(N,PredFun)\n",
		 "Select last N messages to file:\n",
		 "\tselect2file(File,N,WishList)\n",
		 "\tselect2file(File,N,Level)\n",
		 "\tselect2file(File,N,PredFun)\n",
		 "Parameters:\n",
		 "\tN\t - a number of last events to select\n",
		 "\tWishList - a list of wishes i.e. [{WishTag,Value}]\n",
		 "\tLevel\t - an atom one of err, warn, info, all\n",
		 "\tPredFun\t - a custom predicate function fun(Entry)->bool()\n",
		 "\tFile\t - a file name e.g. \"./save.err\"\n",
		 "\tWishTag\t - an atom one of lvl,node,gnode,pid,gpid,ts_gr,ts_ls,grep.",
		 "\n\t\t   {node|gnode,Node}, where Node is an atom like 'n2@host'",
		 "\n\t\t   {pid|gpid,Pid}, where Pid obtained with pid(A,B,C) or string like \"<1.2.3>\"",
		 "\n\t\t   {ts_gr|ts_ls,DateTime}, where DateTime is a tuple. See Notes section.",
		 "\n\t\t   {grep,RegExp}, where RegExp is a string specifying pattern. See Notes section.\n",
		 "Notes:\n",
		 "\tDate Time- a wishes like ts_gr, ts_ls expect value in format ",
		 "\n\t\t   {{Year, Month, Day},{Hour, Minute, Second}}",
		 "\n\t\t   e.g. {{2009,6,17},{13,39,55}}.\n\t\t   See also calendar:local_time()\n",
		 "\tRegExp\t - is a regular expression in a Perl notation"
		],
	io:format("~s",[lists:flatten(Hlp)]).

%%====================================================================
%% Internal functions
%%====================================================================
select(N,PredFun) when is_function(PredFun, 1)->
	lists:reverse(filter_loop(start,N,PredFun,[])).

filter_loop(Cont,N,PredFun,Entries) ->
	case disk_log:chunk(?LOG_NAME, Cont, N) of
		{error, Reason} ->
			throw(Reason);
		{Cont2,Terms} ->
			Entries2=entries2(N,PredFun,Entries,Terms),
			filter_loop(Cont2,N,PredFun,Entries2);
		{Cont2, Terms, _Badbytes} ->
			Entries2=entries2(N,PredFun,Entries,Terms),
			filter_loop(Cont2,N,PredFun,Entries2);
		eof ->
			Entries
	end.

entries2(N,PredFun,Entries,Terms) ->
	cut(lists:foldl(fun(Entry,AccIn)->
								case PredFun(Entry) of
									true ->
										[Entry|AccIn];
									false ->
										AccIn
								end
						end, Entries, Terms),N).

cut(List,N) when length(List)=<N ->
	List;
cut(List,N) when length(List)>N ->
	{Head,_}=lists:split(N, List),
	Head.

pf_list(_Entry,[]=_PfList) ->
	true;
pf_list(Entry,[Pf|Tail]=_PfList) ->
	case Pf(Entry) of
		false ->
			false;
		true ->
			pf_list(Entry,Tail)
	end.

mpl_parse(MetaPropList) ->
	PfList=lists:foldr(
	  fun(MetaProp,AccIn)->
			  [make_pf(MetaProp)|AccIn]
	  end, [], MetaPropList),
	fun(Entry) ->
			pf_list(Entry,PfList)
	end.
%%{err,now(),node(Gleader),node(Pid)} 
make_pf({lvl,Val}) ->
	fun(_Entry={_Event,_Meta={Level,_,_,_}}) when Level==Val ->
		true;
		(_) ->
			false
	end;
make_pf({ts_gr,Val}) ->
	fun(_Entry={_Event,_Meta={_,Ts,_,_}}) ->
		Dt=calendar:now_to_local_time(Ts),
		if
			Dt>Val ->
				true;
			true ->
				false
		end
	end;
make_pf({ts_ls,Val}) ->
	fun(_Entry={_Event,_Meta={_,Ts,_,_}}) ->
		Dt=calendar:now_to_local_time(Ts),
		if
			Dt<Val ->
				true;
			true ->
				false
		end
	end;
make_pf({gnode,Val}) ->
	fun(_Entry={_Event,_Meta={_,_,GNode,_}}) when GNode==Val ->
		true;
		(_) ->
			false
	end;
make_pf({node,Val}) ->
	fun(_Entry={_Event,_Meta={_,_,_,Node}}) when Node==Val ->
		true;
		(_) ->
			false
	end;
make_pf({pid,Val}) when is_list(Val) ->
	fun(_Entry={Event,_Meta}) ->
		case pid_to_list(element(1,element(3,Event))) of
			Val ->
				true;
			_ ->
				false
		end
	end;
make_pf({pid,Val}) ->
	ValStr=pid_to_list(Val),
	make_pf({pid,ValStr});
make_pf({gpid,Val}) when is_list(Val) ->
	fun(_Entry={Event,_Meta}) ->
		case pid_to_list(element(1,element(3,Event))) of
			Val ->
				true;
			_ ->
				false
		end
	end;
make_pf({gpid,Val}) ->
	ValStr=pid_to_list(Val),
	make_pf({gpid,ValStr});
make_pf({grep,Val}) when is_list(Val)->
	{ok,Mp}=re:compile(Val),
	fun(Entry) ->
		StrRep=lists:flatten(io_lib:format("~s",[svc_glog:format_entry(Entry)])),
%%  		io:format("~p~n",[StrRep]),
%%  		exit(user),
		case re:run(StrRep, Mp) of
			nomatch ->
				false;
			_ ->
				true
		end
	end;
make_pf({BadWishTag,_Val}=BadWish) ->
	throw({bad_wish_tag,BadWishTag,at,BadWish}).


